#include "SpawnPointBehavior.h"
#include "AIEntityController.h"
#include "SetDestinationEvent.h"
#include "DestinationReachedEvent.h"
#include "AddDestinationEvent.h"
#include "System.h"
#include "GlobalTimer.h"
#include "CameraNode.h"
#include "GameApplication.h"
#include "GraphicalComponent.h"
#include "OrientationComponent.h"
#include "SelectableComponent.h"
#include "RigidBodyComponent.h"
#include "CombatComponent.h"
#include "StatusComponent.h"
#include "Library.h"
#include "BoundingSphere.h"
#include "AIEntityController.h"
#include "PhysicsTask.h"
#include "UpdateTask.h"
#include "AIEntityController.h"
#include "MinionBehavior.h"
#include "TestAbility.h"
#include "TestEffect.h"

#define SECONDS_BETWEEN_SPAWNS	30
#define SPAWN_COUNT				2
#define SECONDS_BETWEEN_ENTITY  2

const HashedString SpawnPointBehavior::type("SpawnPointBehavior");

const HashedString& SpawnPointBehavior::Type() const
{
	return type;
}
SpawnPointBehavior::SpawnPointBehavior( AIEntityController* controller ) : IBehavior(controller), m_spawning(false), m_spawnCounter(0), m_secondsBetweenSpawns(SECONDS_BETWEEN_SPAWNS), m_spawnCount(SPAWN_COUNT), m_secondsBetweenEntity(SECONDS_BETWEEN_ENTITY)
{
	Maths::Vector3D destination;

	m_controller->getPosition(destination);
	m_controller->UpdateDestination(destination);
	m_controller->UpdatePath();
}
//bool SpawnPointBehavior::Destination( IEvent& event )
//{
//	if (event.Type() == SetDestinationEvent::type)
//	{
//		SetDestinationEvent& setDestination = static_cast<SetDestinationEvent&>(event);
//		
//		Maths::Vector3D destination = setDestination.Destination();
//
//		m_controller->UpdateDestination(destination);
//		m_controller->UpdatePath();	
//	}
//	return false;
//}
//bool SpawnPointBehavior::DestinationReached( IEvent& event )
//{
//	if (event.Type() == DestinationReachedEvent::type)
//	{
//		std::list<Maths::Vector3D>& waypoints = m_controller->getWaypoints();
//		if (waypoints.size() > 0)
//		{
//			Maths::Vector3D next = waypoints.front();
//			waypoints.pop_front();
//
//			m_controller->ReceiveEvent(SetDestinationEvent(next));
//		}
//	}
//	return false;
//}
void SpawnPointBehavior::EnemyGroupName( HashedString name )
{
	m_enemyGroupName = name;
}
void SpawnPointBehavior::GroupName( HashedString name )
{
	m_groupName = name;
}
bool SpawnPointBehavior::AddDestination( IEvent& event )
{
	if (event.Type() == AddDestinationEvent::type)
	{
		AddDestinationEvent& addDestination = static_cast<AddDestinationEvent&>(event);
		Maths::Vector3D destination = addDestination.Destination();

		std::list<Maths::Vector3D>& waypoints = m_controller->getWaypoints();
		waypoints.push_back(destination);
	}
	return false;
}

void SpawnPointBehavior::Update()
{	
	if (!m_spawning)
	{
		if (GlobalTimer::getTime().getSecondsSinceStart() > (m_time.getSecondsSinceStart() + m_secondsBetweenSpawns))
		{
			m_spawning = true;
			m_time = GlobalTimer::getTime();
			m_spawnCounter = 0;
		}
	}
	if (m_spawning)
	{
		if (GlobalTimer::getTime().getSecondsSinceStart() > (m_time.getSecondsSinceStart() + m_secondsBetweenEntity))
		{
			{
				unsigned int entityCounter = Game._getAllEntities().size();
				Pointer<CameraNode>& cameraNode = Game._getMainCamera();
				Pointer<System>& selectables = Game._getSelectables();
				IEntityController::Vector_t& controllers = Game._getControllers();

				char buffer[25];

				Entity *test;
				do
				{
					_itoa_s(entityCounter++, buffer, 10);
					test = Game.RetrieveEntity(buffer);
				} while (test);


				OrientationComponent* orientation = 0;
				GraphicalComponent* graphical = 0;
				SelectableComponent* selectable = 0;
				RigidBodyComponent* rigidbody = 0;
				StatusComponent* stats = 0;
				CombatComponent* combat = 0;
	
				Pointer<IResource> resource;
				Pointer<Model> model;
				Pointer<IComponent> component;
				Pointer<Entity> entity = new Entity();
				Maths::Vector3D position;
				// OrientationCompenent
				component = orientation = new OrientationComponent(entity);
	
				m_controller->getPosition(position);						// My Position
				orientation->getPosition() = position;

				entity->RegisterComponent(OrientationComponent::type, component);
	
				// Find Model

				if ( m_groupName == HashedString("RedTeam"))
					resource = Library::getSingleton().getResource(Model::type, HashedString("RedSphere"));
				else
					resource = Library::getSingleton().getResource(Model::type, HashedString("BlueSphere"));
				CopyPointer(resource, model);

				// GraphicalComponent
				if (model.isValid())
				{
					component = graphical = new GraphicalComponent(entity, model);
					orientation->getTransformNode()->Push_Back(graphical->getNode());
					{
						bool added = false;
						for (INode::Vector_t::iterator it = cameraNode->getChildren().begin(); it != cameraNode->getChildren().end(); ++it)
						{
							if ((*it)->Type() == HashedString("LightNode"))
							{
								(*it)->Push_Back(orientation->getTransformNode());
								added = true;
								break;
							}
						}
						if (!added)
							cameraNode->Push_Back(orientation->getTransformNode());
					}
					entity->RegisterComponent(GraphicalComponent::type, component);
				}
				// RigidBodyComponent
				component = rigidbody = new RigidBodyComponent(Pointer<RigidBody>(new RigidBody(1.0f, entity)), entity);
				rigidbody->getPosition() = new DoubleValueTrigger<Maths::Vector3D>(&orientation->getPosition(), &rigidbody->getBody()->getPosition());
				rigidbody->getBody()->AddShape(Pointer<IShape>(new BoundingSphere(1.0f)));
				entity->RegisterComponent(RigidBodyComponent::type, component);

				System* world = PhysicsTask::getSingleton().FindSystem(HashedString("World"));
				if (world)
					world->AddRigidBody(rigidbody->getBody());
				// SelectableComponent
				component = selectable = new SelectableComponent(Pointer<RigidBody>(new RigidBody(1.0f, entity)), entity);
				selectable->getPosition() = new DoubleValueTriggerFunctor<Maths::Vector3D, RigidBody>(&orientation->getPosition(), &selectable->getBody()->getPosition(), selectable->getBody(), &RigidBody::setPosition);
				selectable->getBody()->AddShape(Pointer<IShape>(new BoundingSphere(1.0f)));
				entity->RegisterComponent(SelectableComponent::type, component);

				selectables->AddRigidBody(selectable->getBody());

				// Status
				component = stats = new StatusComponent(entity);
				StatusComponent::Status& health = stats->getStatus("HealthPoints");
				health = 40;
				StatusComponent::Status& physicalDamage = stats->getStatus("PhysicalDamage");
				physicalDamage = 10;
				StatusComponent::Status& specialDamage = stats->getStatus("SpecialDamage");
				specialDamage = 5;
				StatusComponent::Status& attackSpeed = stats->getStatus("AttackSpeed");
				attackSpeed = 100;
				StatusComponent::Status& aggressionRange = stats->getStatus("AggressionRange");
				aggressionRange = 20;
				StatusComponent::Status& speed = stats->getStatus("Speed");
				speed = 10;
				
				entity->RegisterComponent(StatusComponent::type, component);
				
				// CombatComponent
				component = combat = new CombatComponent(entity);
				combat->Combatant()->Effects()[HealthCondition::type].push_back(new HealthCondition(entity, combat->Combatant()));
				combat->Combatant()->Abilities().push_back(new MeleeAutoAttack(combat->Combatant()));
				entity->RegisterComponent(CombatComponent::type, component);				

				// AI
				AIEntityController* ai;
				Pointer<IEntityController> controller = ai = new AIEntityController(entity);

				ai->setWorld(Game._getWorlds()["Default"]);
				controllers.push_back(controller);
				UpdateTask::getSingleton().RegisterUpdatable(ai);

				Pointer<MinionBehavior> minion;
				Pointer<IBehavior> beh = new MinionBehavior(ai);
				CopyPointer(beh, minion);

				ai->Behavior(beh);
				ai->RegisterHandler(SetDestinationEvent::type, new CEventHandler<MinionBehavior>(minion, &MinionBehavior::Destination));
				ai->RegisterHandler(AddDestinationEvent::type, new CEventHandler<MinionBehavior>(minion, &MinionBehavior::AddDestination));
				ai->RegisterHandler(DestinationReachedEvent::type, new CEventHandler<MinionBehavior>(minion, &MinionBehavior::DestinationReached));

				std::list<Maths::Vector3D>& waypoints = ai->getWaypoints();
				waypoints = m_controller->getWaypoints();

				Game.RegisterEntityToGroup(m_groupName, entity);
				minion->EnemyGroup(m_enemyGroupName);
				
				Game.RegisterEntity(buffer, entity);
			}
			

			m_time = GlobalTimer::getTime();
			++m_spawnCounter;
			if (m_spawnCounter >= SPAWN_COUNT)
			{
				m_spawning = false;
			}
		}
	}
}

bool SpawnPointBehavior::ToXML( tinyxml2::XMLPrinter& printer )
{
	printer.OpenElement("SpawnPointBehavior");

	printer.PushAttribute("EnemyGroupName", m_enemyGroupName.getString().c_str());
	printer.PushAttribute("GroupName", m_groupName.getString().c_str());
	printer.PushAttribute("SecondsBetweenEntity", m_secondsBetweenEntity);
	printer.PushAttribute("SecondsBetweenSpawns", m_secondsBetweenSpawns);
	printer.PushAttribute("Spawning", m_spawning);
	printer.PushAttribute("SpawnCount", m_spawnCount);
	printer.PushAttribute("SpawnCounter", m_spawnCounter);
	printer.PushAttribute("SpawnTime", m_time.getMilliSecondsSinceStart());

	printer.CloseElement();
	return true;
}

bool SpawnPointBehavior::FromXML( tinyxml2::XMLElement* element )
{
	tinyxml2::XMLElement* behaviorElement = element->FirstChildElement("SpawnPointBehavior");

	m_enemyGroupName = HashedString(behaviorElement->Attribute("EnemyGroupName"));
	m_groupName = HashedString(behaviorElement->Attribute("GroupName"));
	m_secondsBetweenEntity = behaviorElement->FloatAttribute("SecondsBetweenEntity");
	m_secondsBetweenSpawns = behaviorElement->FloatAttribute("SecondsBetweenSpawns");
	m_spawning = behaviorElement->BoolAttribute("Spawning");
	m_spawnCount = behaviorElement->UnsignedAttribute("SpawnCount");
	m_spawnCounter = behaviorElement->UnsignedAttribute("SpawnCounter");
	m_time.getMilliSecondsSinceStart() = behaviorElement->FloatAttribute("SpawnTime");

	return true;
}